"use strict";

var $ = require("jquery"),
    Class = require("../../core/class"),
    commonUtils = require("../../core/utils/common");

var PIVOTGRID_EXPAND_CLASS = "dx-expand";

var getRealElementWidth = function(element) {
    var width = 0,
        clientRect;

    if(element.getBoundingClientRect) {
        clientRect = element.getBoundingClientRect();
        width = clientRect.width;

        if(!width) {
            width = clientRect.right - clientRect.left;
        }
    }

    if(width > 0) {
        return width;
    } else {
        return element.offsetWidth;
    }
};

function getFakeTableOffset(scrollPos, elementOffset, tableSize, viewPortSize) {
    var offset = 0,
        halfTableCount = 0,
        halfTableSize = tableSize / 2;

    if(scrollPos + viewPortSize - (elementOffset + tableSize) > 1) {

        if(scrollPos >= elementOffset + tableSize + halfTableSize) {
            halfTableCount = parseInt((scrollPos - (elementOffset + tableSize)) / halfTableSize, 10);
        }

        offset = (elementOffset + tableSize + (halfTableSize) * halfTableCount);
    } else if(scrollPos < elementOffset) {

        if(scrollPos <= elementOffset - halfTableSize) {
            halfTableCount = parseInt((scrollPos - (elementOffset - halfTableSize)) / halfTableSize, 10);
        }

        offset = elementOffset - (tableSize - (halfTableSize) * halfTableCount);
    } else {
        offset = elementOffset;
    }

    return offset;
}

///#DEBUG
exports.getRealElementWidth = getRealElementWidth;
///#ENDDEBUG

exports.AreaItem = Class.inherit({
    _getRowElement: function(index) {
        var that = this;
        if(that._tableElement && that._tableElement.length > 0) {
            return that._tableElement[0].rows[index];
        }
        return null;
    },

    _createGroupElement: function() {
        return $('<div>');
    },

    _createTableElement: function() {
        return $('<table>');
    },

    _getCellText: function(cell, encodeHtml) {
        var cellText = cell.isWhiteSpace ? "" : cell.text || '';

        if(encodeHtml && (cellText.indexOf("<") !== -1 || cellText.indexOf(">") !== -1)) {
            cellText = $("<div>").text(cellText).html();
        }

        return cellText;
    },

    _getRowClassNames: function() {

    },

    _applyCustomStyles: function(options) {
        if(options.cell.width) {
            options.cssArray.push("min-width:" + options.cell.width + "px");
        }
        if(options.cell.sorted) {
            options.classArray.push("dx-pivotgrid-sorted");
        }
    },

    _getMainElementMarkup: function() {
        return "<tbody>";
    },

    _getCloseMainElementMarkup: function() {
        return "</tbody>";
    },

    _renderTableContent: function(tableElement, data) {
        var that = this,
            rowsCount = data.length,
            row,
            cell,
            i,
            j,
            rowElement,
            cellElement,
            cellText,
            rtlEnabled = that.option("rtlEnabled"),
            markupArray = [],
            encodeHtml = that.option("encodeHtml"),
            rowClassNames,
            colspan = "colspan='",
            rowspan = "rowspan='";

        tableElement.data("area", that._getAreaName());
        tableElement.data("data", data);

        tableElement.css("width", "");

        markupArray.push(that._getMainElementMarkup());

        for(i = 0; i < rowsCount; i++) {
            row = data[i];
            var columnMarkupArray = [];
            rowClassNames = [];

            markupArray.push("<tr ");

            for(j = 0; j < row.length; j++) {

                cell = row[j];

                this._getRowClassNames(i, cell, rowClassNames);

                columnMarkupArray.push("<td ");


                if(cell) {
                    cell.rowspan && columnMarkupArray.push(rowspan + (cell.rowspan || 1) + "'");
                    cell.colspan && columnMarkupArray.push(colspan + (cell.colspan || 1) + "'");

                    var styleOptions = {
                        cellElement: cellElement,
                        cell: cell,
                        cellsCount: row.length,
                        cellIndex: j,
                        rowElement: rowElement,
                        rowIndex: i,
                        rowsCount: rowsCount,
                        rtlEnabled: rtlEnabled,
                        classArray: [],
                        cssArray: []
                    };

                    that._applyCustomStyles(styleOptions);

                    if(styleOptions.cssArray.length) {
                        columnMarkupArray.push("style='");
                        columnMarkupArray.push(styleOptions.cssArray.join(";"));
                        columnMarkupArray.push("'");
                    }

                    if(styleOptions.classArray.length) {
                        columnMarkupArray.push("class='");
                        columnMarkupArray.push(styleOptions.classArray.join(" "));
                        columnMarkupArray.push("'");
                    }

                    columnMarkupArray.push(">");

                    if(commonUtils.isDefined(cell.expanded)) {
                        columnMarkupArray.push("<div class='dx-expand-icon-container'><span class='" + PIVOTGRID_EXPAND_CLASS + "'></span></div>");
                    }

                    cellText = this._getCellText(cell, encodeHtml);

                } else {
                    cellText = "";
                }

                columnMarkupArray.push("<span ");

                if(commonUtils.isDefined(cell.wordWrapEnabled)) {
                    columnMarkupArray.push("style='white-space:", cell.wordWrapEnabled ? "normal" : "nowrap", ";'");
                }

                columnMarkupArray.push(">" + cellText + "</span>");

                if(cell.sorted) {
                    columnMarkupArray.push("<span class='dx-icon-sorted'></span>");
                }

                columnMarkupArray.push("</td>");
            }

            if(rowClassNames.length) {
                markupArray.push("class='");
                markupArray.push(rowClassNames.join(" "));
                markupArray.push("'");
            }
            markupArray.push(">");
            markupArray.push(columnMarkupArray.join(""));
            markupArray.push("</tr>");
        }

        markupArray.push(this._getCloseMainElementMarkup());

        tableElement.append(markupArray.join(""));

        this._triggerOnCellPrepared(tableElement, data);
    },

    _triggerOnCellPrepared: function(tableElement, data) {
        var that = this,
            rowElements = tableElement.find("tr"),
            areaName = that._getAreaName(),
            onCellPrepared = that.option("onCellPrepared"),
            hasEvent = that.component.hasEvent("cellPrepared"),
            rowElement,
            cellElement,
            onCellPreparedArgs,
            defaultActionArgs = this.component._defaultActionArgs(),
            row,
            cell,
            rowIndex,
            columnIndex;

        if(onCellPrepared || hasEvent) {
            for(rowIndex = 0; rowIndex < data.length; rowIndex++) {
                row = data[rowIndex];
                rowElement = rowElements.eq(rowIndex);

                for(columnIndex = 0; columnIndex < row.length; columnIndex++) {
                    cell = row[columnIndex];
                    cellElement = rowElement.children().eq(columnIndex);
                    onCellPreparedArgs = {
                        area: areaName,
                        rowIndex: rowIndex,
                        columnIndex: columnIndex,
                        cellElement: cellElement,
                        cell: cell
                    };
                    if(hasEvent) {
                        that.component._trigger("onCellPrepared", onCellPreparedArgs);
                    } else {
                        onCellPrepared($.extend(onCellPreparedArgs, defaultActionArgs));
                    }
                }
            }
        }
    },

    _getRowHeight: function(index) {
        var row = this._getRowElement(index),
            clientRect,
            height = 0;

        if(row && row.lastChild) {
            if(row.getBoundingClientRect) {
                clientRect = row.getBoundingClientRect();
                height = clientRect.height;
            }
            if(height > 0) {
                return height;
            } else {
                return row.offsetHeight;
            }
        }
        return 0;
    },

    _setRowHeight: function(index, value) {
        var row = this._getRowElement(index);
        if(row) {
            row.style.height = value + 'px';
        }
    },

    ctor: function(component) {
        this.component = component;
    },

    option: function() {
        return this.component.option.apply(this.component, arguments);
    },

    getRowsLength: function() {
        var that = this;
        if(that._tableElement && that._tableElement.length > 0) {
            return that._tableElement[0].rows.length;
        }
        return 0;
    },

    getRowsHeight: function() {
        var that = this,
            result = [],
            rowsLength = that.getRowsLength(),
            i;

        for(i = 0; i < rowsLength; i++) {
            result.push(that._getRowHeight(i));
        }
        return result;
    },

    setRowsHeight: function(values) {
        var that = this,
            totalHeight = 0,
            valuesLength = values.length,
            i;

        for(i = 0; i < valuesLength; i++) {
            totalHeight += values[i];
            that._setRowHeight(i, values[i]);
        }
        this._tableHeight = totalHeight;
        this._tableElement[0].style.height = totalHeight + 'px';
    },

    getColumnsWidth: function() {
        var rowsLength = this.getRowsLength(),
            rowIndex,
            row,
            i,
            columnIndex,
            processedCells = [],
            result = [],
            fillCells = function(cells, rowIndex, columnIndex, rowSpan, colSpan) {
                var rowOffset, columnOffset;
                for(rowOffset = 0; rowOffset < rowSpan; rowOffset++) {
                    for(columnOffset = 0; columnOffset < colSpan; columnOffset++) {
                        cells[rowIndex + rowOffset] = cells[rowIndex + rowOffset] || [];
                        cells[rowIndex + rowOffset][columnIndex + columnOffset] = true;
                    }
                }
            };

        if(rowsLength) {
            for(rowIndex = 0; rowIndex < rowsLength; rowIndex++) {
                processedCells[rowIndex] = processedCells[rowIndex] || [];
                row = this._getRowElement(rowIndex);
                for(i = 0; i < row.cells.length; i++) {
                    for(columnIndex = 0; processedCells[rowIndex][columnIndex]; columnIndex++);
                    fillCells(processedCells, rowIndex, columnIndex, row.cells[i].rowSpan, row.cells[i].colSpan);
                    if(row.cells[i].colSpan === 1) {
                        result[columnIndex] = result[columnIndex] || getRealElementWidth(row.cells[i]);
                    }
                }

            }
        }
        return result;
    },

    setColumnsWidth: function(values) {
        var i,
            totalWidth = 0,
            tableElement = this._tableElement[0],
            colgroupElementHTML = '',
            columnsCount = this.getColumnsCount(),
            columnWidth = [];

        for(i = 0; i < columnsCount; i++) {
            columnWidth.push(values[i] || 0);
        }

        for(i = columnsCount; i < values.length && values; i++) {
            columnWidth[columnsCount - 1] += values[i];
        }

        for(i = 0; i < columnsCount; i++) {
            totalWidth += columnWidth[i];
            colgroupElementHTML += '<col style="width: ' + columnWidth[i] + 'px">';
        }
        this._colgroupElement.html(colgroupElementHTML);
        this._tableWidth = totalWidth;
        tableElement.style.width = totalWidth + 'px';
        tableElement.style.tableLayout = 'fixed';
    },

    resetColumnsWidth: function() {
        this._colgroupElement.find('col').width('auto');
        this._tableElement.css({
            width: '',
            tableLayout: ''
        });
    },

    groupWidth: function(value) {
        if(value === undefined) {
            return this._groupElement.width();
        } else if(value >= 0) {
            this._groupWidth = value;
            return (this._groupElement[0].style.width = value + 'px');
        } else {
            return (this._groupElement[0].style.width = value);
        }
    },

    groupHeight: function(value) {
        if(value === undefined) {
            return this._groupElement.height();
        }

        this._groupHeight = null;

        if(value >= 0) {
            this._groupHeight = value;
            this._groupElement[0].style.height = value + 'px';
        } else {
            this._groupElement[0].style.height = value;
        }
    },

    groupElement: function() {
        return this._groupElement;
    },

    tableElement: function() {
        return this._tableElement;
    },

    element: function() {
        return this._rootElement;
    },

    headElement: function() {
        return this._tableElement.find('thead');
    },

    setVirtualContentParams: function(params) {
        this._virtualContent.css({
            width: params.width,
            height: params.height
        });

        this.groupElement().addClass("dx-virtual-mode");
    },

    disableVirtualMode: function() {
        this.groupElement().removeClass("dx-virtual-mode");
    },

    _renderVirtualContent: function() {
        var that = this;
        if(!that._virtualContent && that.option("scrolling.mode") === "virtual") {
            that._virtualContent = $("<div>").addClass("dx-virtual-content").insertBefore(that._tableElement);
        }
    },

    reset: function() {
        var that = this,
            tableElement = that._tableElement[0];

        that._fakeTable && that._fakeTable.detach();
        that._fakeTable = null;

        that.disableVirtualMode();
        that.groupWidth("100%");
        that.groupHeight("auto");

        that.resetColumnsWidth();

        if(tableElement) {
            for(var i = 0; i < tableElement.rows.length; i++) {
                tableElement.rows[i].style.height = "";
            }
            tableElement.style.height = "";
            tableElement.style.width = "100%";
        }
    },

    _updateFakeTableVisibility: function() {
        var that = this,
            tableElement = that.tableElement()[0],
            fakeTableElement = that._fakeTable[0];

        if(tableElement.style.top === fakeTableElement.style.top && fakeTableElement.style.left === tableElement.style.left) {
            that._fakeTable.addClass("dx-hidden");
        } else {
            that._fakeTable.removeClass("dx-hidden");
        }
    },

    _moveFakeTableLeft: function(scrollPos) {
        var that = this,
            tableElementOffsetLeft = parseFloat(that.tableElement()[0].style.left),
            offsetLeft = getFakeTableOffset(scrollPos, tableElementOffsetLeft, that._tableWidth, that._groupWidth);

        if(parseFloat(that._fakeTable[0].style.left) !== offsetLeft) {
            that._fakeTable[0].style.left = offsetLeft + "px";
        }
    },

    _moveFakeTableTop: function(scrollPos) {
        var that = this,
            tableElementOffsetTop = parseFloat(that.tableElement()[0].style.top),
            offsetTop = getFakeTableOffset(scrollPos, tableElementOffsetTop, that._tableHeight, that._groupHeight);

        if(parseFloat(that._fakeTable[0].style.top) !== offsetTop) {
            that._fakeTable[0].style.top = offsetTop + "px";
        }
    },

    _moveFakeTable: function() {
        this._updateFakeTableVisibility();
    },

    _createFakeTable: function() {
        var that = this;

        if(!that._fakeTable) {
            that._fakeTable = that.tableElement()
                .clone()
                .addClass("dx-pivot-grid-fake-table")
                .appendTo(that._virtualContent);
        }
    },

    render: function(rootElement, data) {
        var that = this;

        if(that._tableElement) {
            try {
                that._tableElement[0].innerHTML = '';
            } catch(e) {
                that._tableElement.empty();
            }
            that._tableElement.attr('style', '');
        } else {
            that._groupElement = that._createGroupElement();
            that._tableElement = that._createTableElement();

            that._tableElement.appendTo(that._groupElement);
            that._groupElement.appendTo(rootElement);
            that._rootElement = rootElement;
        }

        that._colgroupElement = $('<colgroup>').appendTo(that._tableElement);
        that._renderTableContent(that._tableElement, data);

        that._renderVirtualContent();
    },

    _getScrollable: function() {
        return this.groupElement().data('dxScrollable');
    },

    on: function(eventName, handler) {
        var scrollable = this._getScrollable();
        if(scrollable) {
            scrollable.on(eventName, handler);
        }
        return this;
    },

    off: function() {
        var scrollable = this._getScrollable();
        if(scrollable) {
            scrollable.off.apply(scrollable, arguments);
        }
        return this;
    },

    scrollTo: function(pos) {
        var scrollable = this._getScrollable();

        if(scrollable) {
            scrollable.scrollTo(pos);
            if(this._virtualContent) {
                this._createFakeTable();
                this._moveFakeTable(pos);
            }
        }
    },

    updateScrollable: function() {
        var scrollable = this._getScrollable();
        if(scrollable) {
            return scrollable.update();
        }
    },

    getColumnsCount: function() {
        var columnCount = 0,
            row = this._getRowElement(0),
            cells;

        if(row) {
            cells = row.cells;
            for(var i = 0, len = cells.length; i < len; ++i) {
                columnCount += cells[i].colSpan;
            }
        }

        return columnCount;
    }
});
